#include "configweave.h"

#include <iostream>
#if CFG_HAVE_STD_RANGES
#include <ranges>
#endif
#include <regex>

using namespace cfg;

// 自定义 Transformer: set_value
// 作用：将 options.value 写入到 options.path（若 path 以 '/' 开头则按 JSON Pointer 写入，否则按
// a.b[2].c 传统路径写入）。 参数：
//   - options.path: 写入路径（必填）
//   - options.value: 字符串值（必填）
// 返回：成功返回 true；失败时也返回 true（错误由 Builder 在 freeze 阶段统一收集）。
class CustomTransformerSetValue : public cfg::ITransformer
{
public:
    bool transform(Builder & builder,
                   const PluginOptions & options,
                   std::vector<ConfigError> & out_errors) override
    {
        (void)out_errors;
        auto it_path = options.find("path");
        auto it_val = options.find("value");
        if (it_path == options.end() || it_val == options.end())
            return true; // 选项缺失时静默跳过

        const std::string & p = it_path->second;
        const std::string & val = it_val->second;
        if (!p.empty() && p[0] == '/')
            builder.set_pointer(p, std::string(val));
        else
            builder.set(p, std::string(val));
        return true;
    }
};

// 自定义 Validator: number_at_path
// 作用：校验指定路径上的值是数值（int64 或 double）。
// 参数：
//   - options.path: 需要校验的路径（支持 JSON Pointer 与 a.b[2].c 两种语法）。
// 诊断：
//   - 缺失 -> Errc::MissingKey
//   - 类型不符 -> Errc::NumberExpected（expected_type=Number, actual_type=实际类型）
class CustomValidatorNumberAtPath : public cfg::IValidator
{
public:
    void validate(const View & view,
                  const PluginOptions & options,
                  std::vector<ConfigError> & out_errors) override
    {
        auto it = options.find("path");
        if (it == options.end())
            return; // 无配置则忽略
        const std::string & p = it->second;
        auto maybe = view.lookup(p);
        if (!maybe)
        {
            out_errors.push_back(
                ConfigError{Errc::MissingKey, std::string("missing required key: ") + p, {p}});
            return;
        }
        // 允许 int64 或 double
        auto as_num = maybe->get_number();
        if (!as_num.has_value())
        {
            auto as_i64 = maybe->get_int64();
            if (!as_i64.has_value())
            {
                ConfigError e{Errc::NumberExpected, std::string("number expected at ") + p, {p}};
                e.expected_type = SchemaValueType::Number;
                e.actual_type = maybe->value_type();
                out_errors.push_back(std::move(e));
            }
        }
    }
};

// 自定义 Validator: regex_match
// 作用：校验指定路径上的字符串满足给定正则表达式。
// 参数：
//   - options.path: 需要校验的路径（支持 JSON Pointer 与 a.b[2].c 两种语法）。
//   - options.pattern: 正则表达式（ECMAScript 语法）。
// 诊断：
//   - 缺失 -> Errc::MissingKey
//   - 不是字符串 -> Errc::StringExpectedForPattern（expected_type=String, actual_type=实际类型）
//   - 正则不匹配 -> Errc::RegexMismatch（expected_pattern=pattern）
//   - 非法正则 -> Errc::InvalidRegex
class CustomValidatorRegexMatch : public cfg::IValidator
{
public:
    void validate(const View & view,
                  const PluginOptions & options,
                  std::vector<ConfigError> & out_errors) override
    {
        auto it_path = options.find("path");
        auto it_pat = options.find("pattern");
        if (it_path == options.end() || it_pat == options.end())
            return;
        const std::string & p = it_path->second;
        const std::string & pat = it_pat->second;

        auto maybe = view.lookup(p);
        if (!maybe)
        {
            out_errors.push_back(
                ConfigError{Errc::MissingKey, std::string("missing required key: ") + p, {p}});
            return;
        }
        auto s = maybe->get_string();
        if (!s.has_value())
        {
            ConfigError e{Errc::StringExpectedForPattern,
                          std::string("string expected for pattern at ") + p,
                          {p}};
            e.expected_type = SchemaValueType::String;
            e.actual_type = maybe->value_type();
            out_errors.push_back(std::move(e));
            return;
        }
        try
        {
            std::regex re(pat);
            if (!std::regex_match(*s, re))
            {
                ConfigError e{Errc::RegexMismatch, std::string("regex mismatch at ") + p, {p}};
                e.expected_pattern = pat;
                out_errors.push_back(std::move(e));
            }
        }
        catch (const std::regex_error &)
        {
            ConfigError e{
                Errc::InvalidRegex, std::string("invalid regex in validator at ") + p, {p}};
            e.expected_pattern = pat;
            out_errors.push_back(std::move(e));
        }
    }
};

// 注册自定义插件
REGISTER_TRANSFORMER("set_value", CustomTransformerSetValue);
REGISTER_VALIDATOR("number_at_path", CustomValidatorNumberAtPath);
REGISTER_VALIDATOR("regex_match", CustomValidatorRegexMatch);

// 测试数据文件路径辅助：返回当前测试源文件所在目录的 data/filename
static std::string
test_data_path(const char * filename)
{
    std::string file = __FILE__;
    auto pos = file.find_last_of("/\\");
    std::string dir = (pos == std::string::npos) ? std::string(".") : file.substr(0, pos);
    return dir + "/../tests/data/" + filename;
}

// 示例：最小流水线执行
// - provider: single_file 读取 tests/data/include_sample.yaml
// - transformer: normalize_keys -> snake_case
// - validator: required_key -> 路径 /db/host 必须存在
int
main()
{
    ConfigWeave cw;
    auto r =
        cw.from_file(test_data_path("include_sample.yaml"))
            .normalize_keys("snake_case")
            .add_transformer("set_value", {{"path", "service.name"}, {"value", "demo"}})
            // 写入包含插值占位符的字符串（仅使用配置路径与三元表达式，避免 env 解析歧义）
            .add_transformer("set_value",
                             {{"path", "service.base_url"}, {"value", "http://${db.host}:8080"}})
            .add_transformer(
                "set_value",
                {{"path", "service.endpoint"}, {"value", "${service.base_url}/v1/health"}})
            .add_transformer(
                "set_value",
                {{"path", "service.db_conn"}, {"value", "postgres://${db.host}:${db.port}"}})
            .add_transformer("set_value", {{"path", "user.name"}, {"value", "alice"}})
            .add_transformer("set_value",
                             {{"path", "service.greeting"}, {"value", "Hello, ${user.name:Guest}"}})
            // 环境变量插值演示：$VAR 与 ${env:VAR}
            .add_transformer(
                "set_value",
                {{"path", "service.user_from_dollar"}, {"value", "User=${env:USER:guest}"}})
            .add_transformer(
                "set_value",
                {{"path", "service.user_from_env"}, {"value", "User=${env:USER:guest}"}})
            .add_transformer("set_value",
                             {{"path", "service.hostname_from_dollar"},
                              {"value", "Host=${env:HOSTNAME:unknown-host}"}})
            .add_transformer("set_value",
                             {{"path", "service.hostname_from_env"},
                              {"value", "Host=${env:HOSTNAME:unknown-host}"}})
            .add_transformer("set_value",
                             {{"path", "service.base_url_env"},
                              {"value", "http://${env:HOSTNAME:localhost}:8080"}})
            // 跨节点引用示例
            .add_transformer("set_value", {{"path", "demo.absolute"}, {"value", "${db.host}"}})
            .add_transformer(
                "set_value",
                {{"path", "demo.defaulted"}, {"value", "${maybe.missing.path:default-value}"}})
            .add_transformer("set_value",
                             {{"path", "demo.array_first"}, {"value", "first=${arr[0]}"}})
            .add_transformer("set_value", {{"path", "demo.array_nested"}, {"value", "${arr[2].x}"}})
            .add_transformer(
                "set_value",
                {{"path", "demo.combo_url"},
                 {"value",
                  "http://${service.host:${db.host}}:${service.port:8080}/api/v${api.version:1}"}})
            .add_transformer("set_value", {{"path", "feature.enabled"}, {"value", "true"}})
            .add_transformer("set_value",
                             {{"path", "plan.paid_url"}, {"value", "https://paid.example.com"}})
            .add_transformer("set_value",
                             {{"path", "plan.free_url"}, {"value", "https://free.example.com"}})
            .add_transformer("set_value",
                             {{"path", "demo.plan_url"},
                              {"value", "${feature.enabled?${plan.paid_url}:${plan.free_url}}"}})
            .add_transformer("set_value",
                             {{"path", "demo.region_service"},
                              {"value", "${env:REGION:us-east-1}-${service.name}"}})
            .add_transformer("set_value",
                             {{"path", "demo.cascade_base"}, {"value", "${service.base_url}"}})
            .add_transformer(
                "set_value",
                {{"path", "demo.cascade_health"}, {"value", "${demo.cascade_base}/healthz"}})
            // 调用 interpolate 插件执行插值
            .add_transformer("interpolate", {})
            .require_key("/db/host")
            .add_validator("number_at_path", {{"path", "/db/port"}})
            .add_validator("regex_match", {{"path", "/db/host"}, {"pattern", "^[a-z0-9.-]+$"}})
            // stdout sink 可用选项与默认值：
            //   - format: 输出格式，当前仅支持 "json"；默认值 "json"
            //   - pretty: 是否美化缩进；默认值 false
            //   - indent: 缩进空格数（pretty=true 时生效）；默认值 2
            //   - newline: 末尾是否追加换行；默认值 true
            // 示例 1：显式指定 pretty 与 indent（其余使用默认）
            .add_sink("stdout", {{"pretty", "true"}, {"indent", "2"}})
            .run();

    std::cout << "pipeline result: " << (r.root ? "ok" : "no-root") << "\n";

    View v(r.root);
    auto db_host = v.lookup("/db/host");
    if (db_host)
    {
        auto s = db_host->get_string();
        if (s)
            std::cout << "db.host = " << *s << "\n";
    }

    // 读取自定义 transformer 写入的值
    auto svc = v.lookup("service.name");
    if (svc)
    {
        auto s = svc->get_string();
        if (s)
            std::cout << "service.name = " << *s << "\n";
    }

    // 输出 interpolate 插值后的结果
    auto base_url = v.lookup("service.base_url");
    if (base_url)
    {
        auto s = base_url->get_string();
        if (s)
            std::cout << "service.base_url = " << *s << "\n";
    }
    auto endpoint = v.lookup("service.endpoint");
    if (endpoint)
    {
        auto s = endpoint->get_string();
        if (s)
            std::cout << "service.endpoint = " << *s << "\n";
    }
    auto db_conn = v.lookup("service.db_conn");
    if (db_conn)
    {
        auto s = db_conn->get_string();
        if (s)
            std::cout << "service.db_conn = " << *s << "\n";
    }
    auto greeting = v.lookup("service.greeting");
    if (greeting)
    {
        auto s = greeting->get_string();
        if (s)
            std::cout << "service.greeting = " << *s << "\n";
    }

    // 演示环境变量插值的结果
    auto user_from_dollar = v.lookup("service.user_from_dollar");
    if (user_from_dollar)
    {
        auto s = user_from_dollar->get_string();
        if (s)
            std::cout << "service.user_from_dollar = " << *s << "\n";
    }
    auto user_from_env = v.lookup("service.user_from_env");
    if (user_from_env)
    {
        auto s = user_from_env->get_string();
        if (s)
            std::cout << "service.user_from_env = " << *s << "\n";
    }
    auto hostname_from_dollar = v.lookup("service.hostname_from_dollar");
    if (hostname_from_dollar)
    {
        auto s = hostname_from_dollar->get_string();
        if (s)
            std::cout << "service.hostname_from_dollar = " << *s << "\n";
    }
    auto hostname_from_env = v.lookup("service.hostname_from_env");
    if (hostname_from_env)
    {
        auto s = hostname_from_env->get_string();
        if (s)
            std::cout << "service.hostname_from_env = " << *s << "\n";
    }
    auto base_url_env = v.lookup("service.base_url_env");
    if (base_url_env)
    {
        auto s = base_url_env->get_string();
        if (s)
            std::cout << "service.base_url_env = " << *s << "\n";
    }

    auto demo_absolute = v.lookup("demo.absolute");
    if (demo_absolute)
    {
        auto s = demo_absolute->get_string();
        if (s)
            std::cout << "demo.absolute = " << *s << "\n";
    }
    auto demo_defaulted = v.lookup("demo.defaulted");
    if (demo_defaulted)
    {
        auto s = demo_defaulted->get_string();
        if (s)
            std::cout << "demo.defaulted = " << *s << "\n";
    }
    auto demo_array_first = v.lookup("demo.array_first");
    if (demo_array_first)
    {
        auto s = demo_array_first->get_string();
        if (s)
            std::cout << "demo.array_first = " << *s << "\n";
    }
    auto demo_array_nested = v.lookup("demo.array_nested");
    if (demo_array_nested)
    {
        auto s = demo_array_nested->get_string();
        if (s)
            std::cout << "demo.array_nested = " << *s << "\n";
    }
    auto demo_combo_url = v.lookup("demo.combo_url");
    if (demo_combo_url)
    {
        auto s = demo_combo_url->get_string();
        if (s)
            std::cout << "demo.combo_url = " << *s << "\n";
    }
    auto demo_plan_url = v.lookup("demo.plan_url");
    if (demo_plan_url)
    {
        auto s = demo_plan_url->get_string();
        if (s)
            std::cout << "demo.plan_url = " << *s << "\n";
    }
    auto demo_region_service = v.lookup("demo.region_service");
    if (demo_region_service)
    {
        auto s = demo_region_service->get_string();
        if (s)
            std::cout << "demo.region_service = " << *s << "\n";
    }
    auto demo_cascade_base = v.lookup("demo.cascade_base");
    if (demo_cascade_base)
    {
        auto s = demo_cascade_base->get_string();
        if (s)
            std::cout << "demo.cascade_base = " << *s << "\n";
    }
    auto demo_cascade_health = v.lookup("demo.cascade_health");
    if (demo_cascade_health)
    {
        auto s = demo_cascade_health->get_string();
        if (s)
            std::cout << "demo.cascade_health = " << *s << "\n";
    }

    if (!r.errors.empty())
    {
        std::cout << "errors (" << r.errors.size() << "):\n";
        for (const auto & e : r.errors)
        {
            std::cout << " - code=" << static_cast<int>(e.code) << ", msg=" << e.message;
            if (e.notes)
                std::cout << ", notes=" << *e.notes;
            if (!e.path.empty())
            {
                std::cout << ", path=";
#if CFG_HAVE_STD_RANGES
                for (char ch : e.path | std::views::join_with(std::string_view(".")))
                {
                    std::cout << ch;
                }
#else
                for (size_t i = 0; i < e.path.size(); ++i)
                {
                    if (i > 0)
                        std::cout << '.';
                    std::cout << e.path[i];
                }
#endif
            }
            std::cout << "\n";
        }
    }
    return 0;
}